--- /dev/null
+/* Entry/Delayed Search Entry
+ *
+ * GtkSearchEntry sets up GtkEntries ready for search. Search entries
+ * have their "changed" signal delayed and should be used
+ * when the searched operation is slow such as loads of entries
+ * to search, or online searches.
+ */
+
+#include <gtk/gtk.h>
+
+static GtkWidget *window = NULL;
+
+static void
+search_entry_destroyed (GtkWidget *widget)
+{
+ window = NULL;
+}
+
+static void
+search_changed_cb (GtkSearchEntry *entry,
+ GtkLabel *result_label)
+{
+ const char *text;
+ text = gtk_entry_get_text (GTK_ENTRY (entry));
+ g_message ("search changed: %s", text);
+ gtk_label_set_text (result_label, text ? text : "");
+}
+
+GtkWidget *
+do_search_entry2 (GtkWidget *do_widget)
+{
+ GtkWidget *content_area;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *button;
+
+ if (!window)
+ {
+ window = gtk_dialog_new_with_buttons ("Search Entry #2",
+ GTK_WINDOW (do_widget),
+ 0,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_NONE,
+ NULL);
+ gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+
+ g_signal_connect (window, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (search_entry_destroyed), &window);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
+ gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+ label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (label), "Search entry demo #2");
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ /* Create our entry */
+ entry = gtk_search_entry_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+ /* Result */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
+
+ label = gtk_label_new ("Result:");
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
+ label = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (search_changed_cb), label);
+
+ /* Give the focus to the close button */
+ button = gtk_dialog_get_widget_for_response (GTK_DIALOG (window), GTK_RESPONSE_NONE);
+ gtk_widget_grab_focus (button);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show_all (window);
+ else
+ {
+ gtk_widget_destroy (window);
+ window = NULL;
+ }
+
+ return window;
+}
G_DEFINE_TYPE (GtkSearchEntry, gtk_search_entry, GTK_TYPE_ENTRY)
+typedef struct {
+ guint delayed_changed_id;
+ gboolean in_timeout;
+} GtkSearchEntryPrivate;
+
+/* 150 mseconds of delay */
+#define DELAYED_TIMEOUT_ID 150
+
+/* This widget got created without a private structure, meaning
+ * that we cannot now have one without breaking ABI */
+#define GET_PRIV(e) G_TYPE_INSTANCE_GET_PRIVATE (e, GTK_TYPE_SEARCH_ENTRY, GtkSearchEntryPrivate)
+
+static void
+gtk_search_entry_finalize (GObject *object)
+{
+ GtkSearchEntryPrivate *priv = GET_PRIV (object);
+
+ if (priv->delayed_changed_id > 0)
+ g_source_remove (priv->delayed_changed_id);
+
+ G_OBJECT_CLASS (gtk_search_entry_parent_class)->finalize (object);
+}
+
static void
gtk_search_entry_class_init (GtkSearchEntryClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_search_entry_finalize;
+
+ g_type_class_add_private (klass, sizeof (GtkSearchEntryPrivate));
}
static void
gtk_entry_set_text (entry, "");
}
+static gboolean
+gtk_search_entry_changed_timeout_cb (gpointer user_data)
+{
+ GtkSearchEntry *entry = user_data;
+ GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+
+ priv->in_timeout = TRUE;
+ g_signal_emit_by_name (entry, "changed");
+ priv->delayed_changed_id = 0;
+ priv->in_timeout = FALSE;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+reset_timeout (GtkSearchEntry *entry)
+{
+ GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+
+ if (priv->delayed_changed_id > 0)
+ g_source_remove (priv->delayed_changed_id);
+ priv->delayed_changed_id = g_timeout_add (DELAYED_TIMEOUT_ID,
+ gtk_search_entry_changed_timeout_cb,
+ entry);
+}
+
static void
-search_entry_changed_cb (GtkEntry *entry,
- gpointer user_data)
+search_entry_changed_cb (GtkSearchEntry *entry,
+ gpointer user_data)
{
+ GtkSearchEntryPrivate *priv = GET_PRIV (entry);
const char *str, *icon_name;
gboolean active;
- str = gtk_entry_get_text (entry);
+ /* Update the icons first */
+ str = gtk_entry_get_text (GTK_ENTRY (entry));
if (str == NULL || *str == '\0')
{
"secondary-icon-activatable", active,
"secondary-icon-sensitive", active,
NULL);
+
+ /* Don't stop the emission if it's the timeout
+ * emitting the signal, otherwise we'll get in a loop */
+ if (priv->in_timeout)
+ return;
+
+ /* Queue up the timeout */
+ reset_timeout (entry);
+ g_signal_stop_emission_by_name (entry, "changed");
}
static void
"primary-icon-activatable", FALSE,
"primary-icon-sensitive", FALSE,
NULL);
-
- search_entry_changed_cb (GTK_ENTRY (entry), NULL);
}
/**